طراحی مبتنی بر دامنه را در جاوا اسکریپت تسلط بخشید. الگوی موجودیت ماژول را یاد بگیرید تا برنامههای مقیاسپذیر، قابل آزمایش و قابل نگهداری را با مدلهای شیء دامنه قوی بسازید.
الگوهای موجودیت ماژول جاوا اسکریپت: نگاهی عمیق به مدلسازی شیء دامنه
در دنیای توسعه نرمافزار، بهویژه در اکوسیستم پویای و همیشه در حال تکامل جاوا اسکریپت، ما اغلب سرعت، چارچوبها و ویژگیها را در اولویت قرار میدهیم. ما رابطهای کاربری پیچیده میسازیم، به APIهای بیشماری متصل میشویم و برنامهها را با سرعتی سرسامآور مستقر میکنیم. اما در این عجله، گاهی اوقات هسته اصلی برنامه خود را نادیده میگیریم: دامنه کسبوکار. این میتواند منجر به چیزی شود که اغلب "Big Ball of Mud" نامیده میشود—سیستمی که در آن منطق کسبوکار پراکنده شده، دادهها ساختار نیافتهاند، و ایجاد یک تغییر ساده میتواند آبشاری از اشکالات پیشبینینشده را ایجاد کند.
اینجاست که مدلسازی شیء دامنه وارد میشود. این عمل ایجاد یک مدل غنی و رسا از فضای مشکلی است که در آن کار میکنید. و در جاوا اسکریپت، الگوی موجودیت ماژول یک راه قدرتمند، ظریف و مستقل از چارچوب برای دستیابی به این هدف است. این راهنمای جامع شما را از طریق تئوری، عمل و مزایای این الگو راهنمایی میکند و شما را قادر میسازد تا برنامههایی قویتر، مقیاسپذیرتر و قابل نگهداریتر بسازید.
مدلسازی شیء دامنه چیست؟
قبل از اینکه به خود الگو بپردازیم، اجازه دهید اصطلاحات خود را روشن کنیم. تمایز این مفهوم از مدل شیء سند (DOM) مرورگر بسیار مهم است.
- دامنه: در نرمافزار، 'دامنه' ناحیه موضوعی خاصی است که کسبوکار کاربر به آن تعلق دارد. برای یک برنامه تجارت الکترونیک، دامنه شامل مفاهیمی مانند محصولات، مشتریان، سفارشها و پرداختها میشود. برای یک پلتفرم رسانههای اجتماعی، شامل کاربران، پستها، نظرات و لایکها میشود.
- مدلسازی شیء دامنه: این فرآیند ایجاد یک مدل نرمافزاری است که موجودیتها، رفتارهای آنها و روابط آنها را در آن دامنه کسبوکار نشان میدهد. این در مورد ترجمه مفاهیم دنیای واقعی به کد است.
یک مدل دامنه خوب فقط مجموعهای از کانتینرهای داده نیست. این یک نمایش زنده از قوانین کسبوکار شماست. یک شیء Order نباید فقط فهرستی از موارد را در خود نگه دارد. باید بداند چگونه مجموع خود را محاسبه کند، چگونه یک مورد جدید اضافه کند و آیا میتوان آن را لغو کرد. این کپسولهسازی دادهها و رفتارها کلید ساخت یک هسته برنامه انعطافپذیر است.
مشکل رایج: آنارشی در لایه "مدل"
در بسیاری از برنامههای جاوا اسکریپت، بهویژه برنامههایی که به طور ارگانیک رشد میکنند، لایه 'مدل' اغلب یک فکر ثانویه است. ما اغلب این ضد الگو را میبینیم:
// Somewhere in an API controller or service...
async function createUser(req, res) {
const { email, password, firstName, lastName } = req.body;
// Business logic and validation is scattered here
if (!email || !email.includes('@')) {
return res.status(400).send({ error: 'A valid email is required.' });
}
if (!password || password.length < 8) {
return res.status(400).send({ error: 'Password must be at least 8 characters.' });
}
const user = {
email: email.toLowerCase(),
password: await hashPassword(password), // Some utility function
fullName: `${firstName} ${lastName}`, // Logic for derived data is here
createdAt: new Date()
};
// Now, what is `user`? It's just a plain object.
// Nothing stops another developer from doing this later:
// user.email = 'an-invalid-email';
// user.password = 'short';
await db.users.insert(user);
res.status(201).send(user);
}
این رویکرد چندین مشکل اساسی را ارائه میدهد:
- هیچ منبع واحدی از حقیقت وجود ندارد: قوانین مربوط به آنچه یک 'کاربر' معتبر را تشکیل میدهد در داخل این کنترلر واحد تعریف شده است. اگر بخش دیگری از سیستم نیاز به ایجاد یک کاربر داشته باشد چه؟ آیا منطق را کپی و پیست میکنید؟ این منجر به ناسازگاری و اشکالات میشود.
- مدل دامنه کمخون: شیء `user` فقط یک کیسه 'احمق' از دادهها است. هیچ رفتاری و خودآگاهی ندارد. تمام منطقی که روی آن کار میکند، در خارج زندگی میکند.
- همبستگی کم: منطق ایجاد نام کامل کاربر با رسیدگی به درخواست/پاسخ API و هش کردن رمز عبور ترکیب شده است.
- تست دشوار: برای آزمایش منطق ایجاد کاربر، باید درخواستها و پاسخهای HTTP، پایگاههای داده و توابع هش را شبیهسازی کنید. شما نمیتوانید فقط مفهوم 'user' را به طور جداگانه آزمایش کنید.
- قراردادهای ضمنی: بقیه برنامه فقط باید 'فرض' کند که هر شیئی که نماینده یک کاربر است، شکل خاصی دارد و دادههای آن معتبر است. هیچ تضمینی وجود ندارد.
راهحل: الگوی موجودیت ماژول جاوا اسکریپت
الگوی موجودیت ماژول با استفاده از یک ماژول جاوا اسکریپت استاندارد (یک فایل) برای تعریف همه چیز در مورد یک مفهوم دامنه واحد، این مشکلات را برطرف میکند. این ماژول به منبع قطعی حقیقت برای آن موجودیت تبدیل میشود.
یک موجودیت ماژول معمولاً یک تابع کارخانهای را در معرض دید قرار میدهد. این تابع مسئول ایجاد یک نمونه معتبر از موجودیت است. شیئی که برمیگرداند فقط داده نیست؛ یک شیء دامنه غنی است که دادههای خود، اعتبارسنجی و منطق کسبوکار خود را کپسوله میکند.
ویژگیهای کلیدی یک موجودیت ماژول
- کپسولهسازی: دادهها و توابعی را که بر روی آن دادهها کار میکنند، با هم ترکیب میکند.
- اعتبارسنجی در مرز: اطمینان میدهد که ایجاد یک موجودیت نامعتبر غیرممکن است. از حالت خود محافظت میکند.
- API واضح: مجموعهای تمیز و عمدی از توابع (یک API عمومی) را برای تعامل با موجودیت در معرض دید قرار میدهد، در حالی که جزئیات پیادهسازی داخلی را پنهان میکند.
- تغییرناپذیری: اغلب اشیاء تغییرناپذیر یا فقط خواندنی تولید میکند تا از تغییرات حالت تصادفی جلوگیری کند و رفتار قابل پیشبینی را تضمین کند.
- قابلیت حمل: هیچ وابستگی به چارچوبها (مانند Express، React) یا سیستمهای خارجی (مانند پایگاههای داده، APIها) ندارد. این منطق کسبوکار خالص است.
اجزای اصلی یک موجودیت ماژول
بیایید مفهوم `User` خود را با استفاده از این الگو بازسازی کنیم. ما یک فایل، `user.js` (یا `user.ts` برای کاربران TypeScript) ایجاد میکنیم و آن را گام به گام میسازیم.
1. تابع کارخانه: سازنده شیء شما
به جای کلاسها، ما از یک تابع کارخانهای (به عنوان مثال، `buildUser`) استفاده خواهیم کرد. کارخانهها انعطافپذیری زیادی را ارائه میدهند، از کشمکش با کلمه کلیدی `this` اجتناب میکنند و حالت خصوصی و کپسولهسازی را در جاوا اسکریپت طبیعیتر میکنند.
هدف ما ایجاد تابعی است که دادههای خام را میگیرد و یک شیء User به خوبی شکلیافته و قابل اعتماد را برمیگرداند.
// file: /domain/user.js
export default function buildMakeUser() {
// This inner function is the actual factory.
// It has access to any dependencies passed to buildMakeUser, if needed.
return function makeUser({
id = generateId(), // Let's assume a function to generate a unique ID
firstName,
lastName,
email,
passwordHash,
createdAt = new Date()
}) {
// ... validation and logic will go here ...
const user = {
getId: () => id,
getFirstName: () => firstName,
getLastName: () => lastName,
getEmail: () => email,
getPasswordHash: () => passwordHash,
getCreatedAt: () => createdAt
};
// Using Object.freeze to make the object immutable.
return Object.freeze(user);
}
}
چند چیز را در اینجا متوجه شوید. ما از یک تابع استفاده میکنیم که یک تابع را برمیگرداند (یک تابع مرتبه بالاتر). این یک الگوی قدرتمند برای تزریق وابستگیها است، مانند یک تولیدکننده شناسه منحصربهفرد یا یک کتابخانه اعتبارسنجی، بدون اتصال موجودیت به یک پیادهسازی خاص. در حال حاضر، ما آن را ساده نگه خواهیم داشت.
2. اعتبارسنجی دادهها: محافظ در دروازه
یک موجودیت باید از یکپارچگی خود محافظت کند. ایجاد یک `User` در یک حالت نامعتبر باید غیرممکن باشد. ما اعتبارسنجی را درست در داخل تابع کارخانهای اضافه میکنیم. اگر دادهها نامعتبر باشند، کارخانه باید یک خطا ایجاد کند و به وضوح بیان کند که چه چیزی اشتباه است.
// file: /domain/user.js
export default function buildMakeUser({ Id, isValidEmail, hashPassword }) {
return function makeUser({
id = Id.makeId(),
firstName,
lastName,
email,
password, // We now take a plain password and handle it inside
createdAt = new Date()
}) {
if (!Id.isValidId(id)) {
throw new Error('User must have a valid id.');
}
if (!firstName || firstName.length < 2) {
throw new Error('First name must be at least 2 characters long.');
}
if (!lastName || lastName.length < 2) {
throw new Error('Last name must be at least 2 characters long.');
}
if (!email || !isValidEmail(email)) {
throw new Error('User must have a valid email address.');
}
if (!password || password.length < 8) {
throw new Error('Password must be at least 8 characters long.');
}
// Data normalization and transformation happens here
const passwordHash = hashPassword(password);
const normalizedEmail = email.toLowerCase();
return Object.freeze({
getId: () => id,
getFirstName: () => firstName,
getLastName: () => lastName,
getEmail: () => normalizedEmail,
getPasswordHash: () => passwordHash,
getCreatedAt: () => createdAt
});
}
}
اکنون، هر بخش از سیستم ما که میخواهد یک `User` ایجاد کند باید از طریق این کارخانه عبور کند. ما در هر بار اعتبارسنجی تضمین شده را دریافت میکنیم. ما همچنین منطق هش کردن رمز عبور و نرمالسازی آدرس ایمیل را کپسوله کردهایم. بقیه برنامه نیازی به دانستن یا اهمیت دادن به این جزئیات ندارند.
3. منطق کسبوکار: کپسولهسازی رفتار
شیء `User` ما هنوز کمی کمخون است. دادهها را نگه میدارد، اما هیچ کاری انجام نمیدهد. بیایید رفتار—متدهایی را که عملکردهای خاص دامنه را نشان میدهند—اضافه کنیم.
// ... inside the makeUser function ...
if (!password || password.length < 8) {
// ...
}
const passwordHash = hashPassword(password);
const normalizedEmail = email.toLowerCase();
return Object.freeze({
getId: () => id,
getFirstName: () => firstName,
getLastName: () => lastName,
getEmail: () => normalizedEmail,
getPasswordHash: () => passwordHash,
getCreatedAt: () => createdAt,
// Business Logic / Behavior
getFullName: () => `${firstName} ${lastName}`,
// A method that describes a business rule
canVote: () => {
// In some countries, voting age is 18. This is a business rule.
// Let's assume we have a dateOfBirth property.
const age = calculateAge(dateOfBirth);
return age >= 18;
}
});
// ...
منطق `getFullName` دیگر در یک کنترلر تصادفی پراکنده نیست. به خود موجودیت `User` تعلق دارد. هرکسی که یک شیء `User` دارد اکنون میتواند با فراخوانی `user.getFullName()` نام کامل را به طور قابل اعتماد دریافت کند. منطق یک بار، در یک مکان تعریف میشود.
ایجاد یک مثال عملی: یک سیستم ساده تجارت الکترونیک
بیایید این الگو را در یک دامنه بیشتر به هم پیوسته اعمال کنیم. ما یک `Product`، یک `OrderItem` و یک `Order` را مدلسازی خواهیم کرد.
1. مدلسازی موجودیت `Product`
یک محصول دارای نام، قیمت و اطلاعات موجودی است. باید نام داشته باشد و قیمت آن نمیتواند منفی باشد.
// file: /domain/product.js
export default function buildMakeProduct({ Id }) {
return function makeProduct({
id = Id.makeId(),
name,
description,
price,
stock = 0
}) {
if (!Id.isValidId(id)) {
throw new Error('Product must have a valid ID.');
}
if (!name || name.trim().length < 2) {
throw new Error('Product name must be at least 2 characters.');
}
if (isNaN(price) || price <= 0) {
throw new Error('Product must have a price greater than zero.');
}
if (isNaN(stock) || stock < 0) {
throw new Error('Stock must be a non-negative number.');
}
return Object.freeze({
getId: () => id,
getName: () => name,
getDescription: () => description,
getPrice: () => price,
getStock: () => stock,
// Business logic
isAvailable: () => stock > 0,
// A method that modifies state by returning a new instance
reduceStock: (amount) => {
if (amount > stock) {
throw new Error('Not enough stock available.');
}
// Return a NEW product object with the updated stock
return makeProduct({ id, name, description, price, stock: stock - amount });
}
});
}
}
به متد `reduceStock` توجه کنید. این یک مفهوم حیاتی مرتبط با تغییرناپذیری است. به جای تغییر ویژگی `stock` در شیء موجود، یک نمونه `Product` *جدید* با مقدار به روز شده برمیگرداند. این باعث میشود که تغییرات حالت صریح و قابل پیشبینی باشند.
2. مدلسازی موجودیت `Order` (ریشه جمع)
یک `Order` پیچیدهتر است. این چیزی است که طراحی مبتنی بر دامنه (DDD) آن را "ریشه جمع" مینامد. این یک موجودیت است که اشیاء دیگر و کوچکتر را در محدوده خود مدیریت میکند. یک `Order` شامل لیستی از `OrderItem`ها میشود. شما یک محصول را مستقیماً به یک سفارش اضافه نمیکنید. شما یک `OrderItem` اضافه میکنید که شامل یک محصول و یک کمیت است.
// file: /domain/order.js
export const ORDER_STATUS = {
PENDING: 'PENDING',
PAID: 'PAID',
SHIPPED: 'SHIPPED',
CANCELLED: 'CANCELLED'
};
export default function buildMakeOrder({ Id, validateOrderItem }) {
return function makeOrder({
id = Id.makeId(),
customerId,
items = [],
status = ORDER_STATUS.PENDING,
createdAt = new Date()
}) {
if (!Id.isValidId(id)) {
throw new Error('Order must have a valid ID.');
}
if (!customerId) {
throw new Error('Order must have a customer ID.');
}
let orderItems = [...items]; // Create a private copy to manage
return Object.freeze({
getId: () => id,
getCustomerId: () => customerId,
getItems: () => [...orderItems], // Return a copy to prevent external modification
getStatus: () => status,
getCreatedAt: () => createdAt,
// Business Logic
calculateTotal: () => {
return orderItems.reduce((total, item) => {
return total + (item.getPrice() * item.getQuantity());
}, 0);
},
addItem: (item) => {
// validateOrderItem is a function that ensures the item is a valid OrderItem entity
validateOrderItem(item);
// Business rule: prevent adding duplicates, just increase quantity
const existingItemIndex = orderItems.findIndex(i => i.getProductId() === item.getProductId());
if (existingItemIndex > -1) {
const newQuantity = orderItems[existingItemIndex].getQuantity() + item.getQuantity();
// Here you'd update the quantity on the existing item
// (This requires items to be mutable or have an update method)
} else {
orderItems.push(item);
}
},
markPaid: () => {
if (status !== ORDER_STATUS.PENDING) {
throw new Error('Only pending orders can be marked as paid.');
}
// Return a new Order instance with the updated status
return makeOrder({ id, customerId, items: orderItems, status: ORDER_STATUS.PAID, createdAt });
}
});
}
}
این موجودیت `Order` اکنون قوانین کسبوکار پیچیدهای را اجرا میکند:
- لیست اقلام خود را مدیریت میکند.
- میداند چگونه مجموع خود را محاسبه کند.
- انتقالهای حالت را اجرا میکند (به عنوان مثال، فقط میتوانید یک سفارش `PENDING` را به عنوان `PAID` علامتگذاری کنید).
منطق کسبوکار برای سفارشها اکنون در این ماژول بهطور مرتب محصور شده است، به طور جداگانه قابل آزمایش است و در کل برنامه شما قابل استفاده مجدد است.
الگوهای پیشرفته و ملاحظات
تغییرناپذیری: سنگ بنای قابلیت پیشبینی
ما به تغییرناپذیری اشاره کردیم. چرا اینقدر مهم است؟ هنگامی که اشیاء تغییرناپذیر هستند، میتوانید آنها را در سراسر برنامه خود پاس دهید بدون اینکه بترسید که برخی از توابع دوردست حالت آنها را به طور غیرمنتظرهای تغییر دهند. این یک کلاس کامل از اشکالات را از بین میبرد و جریان دادههای برنامه شما را بسیار آسانتر میکند.
Object.freeze() یک فریز کم عمق را ارائه میدهد. برای موجودیتهایی که اشیاء یا آرایههای تودرتو دارند (مانند `Order` ما)، باید مراقبتر باشید. به عنوان مثال، در `order.getItems()`، ما یک کپی ([...orderItems]) برگرداندیم تا از فشار دادن موارد به طور مستقیم به آرایه داخلی سفارش توسط فراخوان جلوگیری کنیم.
برای برنامههای پیچیده، کتابخانههایی مانند Immer میتواند کار با ساختارهای تو در توی تغییرناپذیر را بسیار آسانتر کند، اما اصل اصلی باقی میماند: با موجودیتهای خود بهعنوان مقادیر تغییرناپذیر رفتار کنید. هنگامی که تغییری باید رخ دهد، یک مقدار جدید ایجاد کنید.
مدیریت عملیات ناهمزمان و پایداری
شما ممکن است متوجه شده باشید که موجودیتهای ما کاملاً همزمان هستند. آنها هیچ چیز در مورد پایگاههای داده یا APIها نمیدانند. این عمدی است و یک نقطه قوت اصلی این الگو است!
موجودیتها نباید خودشان را ذخیره کنند. وظیفه یک موجودیت اجرای قوانین کسبوکار است. وظیفه ذخیره دادهها در یک پایگاه داده به یک لایه متفاوت از برنامه شما، اغلب به نام لایه سرویس، لایه مورد استفاده، یا الگوی مخزن تعلق دارد.
در اینجا نحوه تعامل آنها آمده است:
// file: /use-cases/create-user.js
// This use case depends on the user entity factory and a database access function.
export default function makeCreateUser({ makeUser, usersDatabase }) {
return async function createUser(userInfo) {
// 1. Create a valid domain entity. This step validates the data.
const user = makeUser(userInfo);
// 2. Check for business rules that require external data (e.g., email uniqueness)
const exists = await usersDatabase.findByEmail({ email: user.getEmail() });
if (exists) {
throw new Error('Email address is already in use.');
}
// 3. Persist the entity. The database needs a plain object.
const persisted = await usersDatabase.insert({
id: user.getId(),
firstName: user.getFirstName(),
// ... and so on
});
return persisted;
}
}
این تفکیک نگرانیها قدرتمند است:
- موجودیت `User` خالص، همزمان و برای آزمایش واحد آسان است.
- مورد استفاده `createUser` مسئول تنظیم است و میتواند با یک پایگاه داده ساختگی آزمایش یکپارچهسازی شود.
- ماژول `usersDatabase` مسئول فناوری پایگاه داده خاص است و میتواند جداگانه آزمایش شود.
سریالسازی و Deserialization
موجودیتهای شما، با متدهایشان، اشیاء غنی هستند. اما وقتی دادهها را از طریق شبکه ارسال میکنید (به عنوان مثال، در پاسخ API JSON) یا آن را در یک پایگاه داده ذخیره میکنید، به یک نمایش داده ساده نیاز دارید. به این فرآیند سریالسازی گفته میشود.
یک الگوی رایج اضافه کردن یک متد `toJSON()` یا `toObject()` به موجودیت شما است.
// ... inside the makeUser function ...
return Object.freeze({
getId: () => id,
// ... other getters
// Serialization method
toObject: () => ({
id,
firstName,
lastName,
email: normalizedEmail,
createdAt
// Notice we don't include the passwordHash
})
});
فرآیند معکوس، گرفتن دادههای ساده از یک پایگاه داده یا API و تبدیل آن به یک موجودیت دامنه غنی، دقیقاً همان چیزی است که تابع کارخانه `makeUser` شما برای آن است. این deserialization است.
تایپ کردن با TypeScript یا JSDoc
در حالی که این الگو کاملاً در جاوا اسکریپت وانیلی کار میکند، افزودن انواع ایستا با TypeScript یا JSDoc آن را فوق شارژ میکند. انواع به شما این امکان را میدهند که "شکل" موجودیت خود را بهطور رسمی تعریف کنید و تکمیل خودکار عالی و بررسیهای زمان کامپایل را ارائه دهید.
// file: /domain/user.ts
// Define the entity's public interface
export type User = Readonly<{
getId: () => string;
getFirstName: () => string;
// ... etc
getFullName: () => string;
}>;
// The factory function now returns the User type
export default function buildMakeUser(...) {
return function makeUser(...): User {
// ... implementation
}
}
مزایای فراگیر الگوی موجودیت ماژول
با اتخاذ این الگو، مزایای متعددی را به دست میآورید که با رشد برنامه شما ترکیب میشوند:
- منبع واحد حقیقت: قوانین کسبوکار و اعتبارسنجی دادهها متمرکز و صریح هستند. تغییری در یک قانون دقیقاً در یک مکان ایجاد میشود.
- همبستگی بالا، اتصال کم: موجودیتها خودکفا هستند و به سیستمهای خارجی متکی نیستند. این باعث میشود که پایگاه کد شما مدولار و آسان برای بازسازی شود.
- قابلیت آزمایش عالی: شما میتوانید تستهای واحد ساده و سریع را برای مهمترین منطق کسبوکار خود بدون مسخره کردن کل جهان بنویسید.
- تجربه توسعهدهنده بهبودیافته: وقتی یک توسعهدهنده نیاز به کار با یک `User` دارد، یک API روشن، قابل پیشبینی و خود مستند دارد. دیگر نیازی به حدس زدن شکل اشیاء ساده نیست.
- یک بنیاد برای مقیاسپذیری: این الگو به شما یک هسته پایدار و قابل اعتماد میدهد. با افزودن ویژگیها، چارچوبها یا اجزای UI بیشتر، منطق کسبوکار شما محافظت شده و سازگار باقی میماند.
نتیجهگیری: یک هسته محکم برای برنامه خود بسازید
در دنیای چارچوبها و کتابخانههایی که سریع در حال حرکت هستند، آسان است که فراموش کنید که این ابزارها گذرا هستند. آنها تغییر خواهند کرد. آنچه دوام میآورد منطق اصلی دامنه کسبوکار شماست. سرمایهگذاری زمان برای مدلسازی صحیح این دامنه فقط یک تمرین آکادمیک نیست. این یکی از مهمترین سرمایهگذاریهای بلندمدت است که میتوانید در سلامت و طول عمر نرمافزار خود انجام دهید.
الگوی موجودیت ماژول جاوا اسکریپت یک راه ساده، قدرتمند و بومی برای پیادهسازی این ایدهها ارائه میدهد. نیازی به یک چارچوب سنگین یا یک راهاندازی پیچیده ندارد. از ویژگیهای اساسی زبان—ماژولها، توابع و بستارها—استفاده میکند تا به شما کمک کند یک هسته تمیز، انعطافپذیر و قابل درک برای برنامه خود بسازید. با یک موجودیت کلیدی در پروژه بعدی خود شروع کنید. ویژگیهای آن را مدلسازی کنید، ایجاد آن را تأیید کنید و به آن رفتار بدهید. شما اولین قدم را به سمت یک معماری نرمافزاری قویتر و حرفهایتر برمیدارید.